using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.Services;
using Microsoft.Practices.CompositeUI.WinForms;
using Microsoft.Practices.ObjectBuilder;
using System.Windows.Forms;
using CompositeUIExtensions.Hosting;
using Microsoft.Samples.Tools.Mdbg;
using Microsoft.Samples.Debugging.MdbgEngine;
using Microsoft.Samples.Debugging.CorDebug;
using Reflector.CodeModel;
using Microsoft.Practices.CompositeUI.EventBroker;
using System.Threading;


namespace DeblectorAddIn.WorkItems
{
    
    public class RootWorkItem:WorkItem
    {
        IAssemblyManager asmManager;
        Reflector.IAssemblyBrowser asmBrowser;
        Reflector.ITranslatorManager transManager;
        Reflector.ILanguageManager langManager;
        ILanguageWriterConfiguration languageConfiguration;
        StateObject _status;
        public StateObject BreakState
        {
            get { return _status; }
        }
        public RootWorkItem()
        {
            _status = new StateObject();
        }
     
        public IServiceProvider Reflector
        {
            get { return Services.Get<IServiceProvider>(); }
        }
        IServiceProvider _reflector;
        Reflector.IAssemblyBrowser _browser;
        protected override void OnRunStarted()
        {
            
            var zsi = new ZoneSmartPartInfo();
            zsi.Title = "IL view";
            TextView tv = new TextView(this);
            tv.Dock = DockStyle.Fill;
            SmartParts.Add(tv, "IL");
            Workspaces["topWorkspace"].Show(tv, zsi);

            Reflector.IWindowManager wman = (Reflector.IWindowManager)Reflector.GetService(typeof(Reflector.IWindowManager));
            /*if (null != wman)
            {
                GenericWindowHost gwh = new GenericWindowHost(wman.Windows["DisassemblerWindow"].Content, "topWorkspace");
                zsi = new TabSmartPartInfo();
                zsi.Title = "Disassembly";
                zsi.ActivateTab = false;
                SmartParts.Add(gwh, "Disassembly");
                Workspaces["topWorkspace"].Show(gwh, zsi);
            }*/
            Application.Idle += new EventHandler(Application_Idle);
            _browser = (Reflector.IAssemblyBrowser)ReflectorApp.GetService(typeof(Reflector.IAssemblyBrowser));
            asmManager = (IAssemblyManager)ReflectorApp.GetService(typeof(IAssemblyManager));
            asmBrowser = (Reflector.IAssemblyBrowser)ReflectorApp.GetService(typeof(Reflector.IAssemblyBrowser));
            transManager = (Reflector.ITranslatorManager)ReflectorApp.GetService(typeof(Reflector.ITranslatorManager));
            langManager = (Reflector.ILanguageManager)ReflectorApp.GetService(typeof(Reflector.ILanguageManager));
            languageConfiguration = new LanguageWriterConfiguration();
            asmBrowser.ActiveItemChanged += new EventHandler(asmBrowser_ActiveItemChanged);
            IAssemblyResolver resolver = asmManager.Resolver;
            asmManager.Resolver = new AssemblyResolver(resolver);
            State["break"] = _status;
            base.OnRunStarted();           
        }

        void asmBrowser_ActiveItemChanged(object sender, EventArgs e)
        {
            IBreakTarget breakTarget = Services.Get<IBreakTarget>();
            ILanguage IL = langManager.Languages[0];
            TextView tv = (TextView)SmartParts["IL"];
            tv.Clear();
            tv.StartBufferize();
            //ITranslator translator = transManager.Disassembler;
            if (asmBrowser.ActiveItem is IMethodDeclaration)
            {
                string target="";
                IMethodDeclaration td = (IMethodDeclaration)asmBrowser.ActiveItem;
                ITypeDeclaration tdecl = td.DeclaringType as ITypeDeclaration;
                if (!string.IsNullOrEmpty(tdecl.Namespace))
                    target += tdecl.Namespace + ".";
                target += tdecl.Name+".";
                target += td.Name;
                breakTarget.ILTarget = target;
                //if( language.Translate )    
                //    td = translator.TranslateMethodDeclaration(asmBrowser.ActiveItem as IMethodDeclaration);
                //ILanguageWriter writer = language.GetWriter(tv, languageConfiguration);
                ILanguageWriter ilWriter = IL.GetWriter(tv, languageConfiguration);
                ilWriter.WriteMethodDeclaration(td);
                tv.UpdateUI();
            }
            else
            {
                tv.Write("Start a program selecting an executable in the left tree\n or attach a running process\nChoose a method or a getter/setter to insert breakpoint in.\nBreakpoints can be inserted only when the program is in pause");
            }
            tv.EndBufferize();
        }

        [EventSubscription("event://debugevent",Thread=ThreadOption.UserInterface)]
        public void OnDebugEvent(object sender,CustomPostCallbackEventArgs e)
        {
            switch (e.CallbackType)
            {
                case ManagedCallbackType.OnCreateProcess:
                    _textBreak.Clear();
                    break;
                case ManagedCallbackType.OnProcessExit:
                    TextView tv = (TextView)SmartParts["IL"];
                    foreach (string s in _textBreak.Values)
                    {
                        tv.RemoveBreakpointUI(s);
                    }
                    break;
            }
        }

        [EventPublication("event://executecommand", PublicationScope.Global)]
        public event EventHandler<CommandEventArgs> ExecuteCommand;
        
        IServiceProvider ReflectorApp
        {
            get { return Services.Get<IServiceProvider>(); }
        }
        string SelectedExe
        {
            get {
                    if (_browser.ActiveItem is IAssembly)
                    {
                        if (((IAssembly)_browser.ActiveItem).Location.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
                            return  ((IAssembly)_browser.ActiveItem).Location;
                    }
                    return null;
            }
        }
        void Application_Idle(object sender, EventArgs e)
        {
            MDbgShell shell = Services.Get<MDbgShell>();
            ToolbarButtons buttons = Services.Get<ToolbarButtons>();
            if (null != shell)
            {
                if (null != buttons)
                {
                    buttons["attach"].Enabled = !shell.Debugger.Processes.HaveActive;
                    buttons["go"].Enabled = (SelectedExe != null && !shell.Debugger.Processes.HaveActive) ||
                                            BreakState.OnBreak == true || BreakState.CanStep == true
                                            ;
                    buttons["break"].Enabled = shell.Debugger.Processes.HaveActive && BreakState.OnBreak==false;
                    buttons["stop"].Enabled = shell.Debugger.Processes.HaveActive;
                    buttons["stepinto"].Enabled = BreakState.CanStep;
                    buttons["stepover"].Enabled = BreakState.CanStep;
                    buttons["stepout"].Enabled = BreakState.CanStep;
                    buttons["breakpoint"].Enabled = shell.Debugger.Processes.HaveActive && (BreakState.CanStep || BreakState.OnBreak);
                    buttons["breakonexception"].Enabled = true;
                }
            }
            
        }
        public void Run_Click(object sender, EventArgs e)
        {
            MDbgShell shell = Services.Get<MDbgShell>();
            if (BreakState.OnBreak == false  && BreakState.CanStep == false)
            {
                if (!shell.Debugger.Processes.HaveActive)
                {
                    ExecuteCommand(this, new CommandEventArgs(string.Format("r \"{0}\"", SelectedExe)));
                }
            }
            else
            {
                ExecuteCommand(this, new CommandEventArgs("go"));
                BreakState.OnBreak = false;
            }
        }
        public ICollection<string> BreakPoints
        {
            get { return _textBreak.Values; }
        }

        private Dictionary<string, string> _textBreak = new Dictionary<string,string>();
        public void ToggleBreak_Click(object sender, EventArgs e)
        {
            MDbgShell shell = Services.Get<MDbgShell>();
            IBreakTarget breakTarget = Services.Get<IBreakTarget>();
            string ilTarget = breakTarget.ILTarget;
            
            if (null != ilTarget)
            {
                if (!_textBreak.ContainsKey(ilTarget))
                {
                    
                    ExecuteCommand(this, new CommandEventArgs("b " + ilTarget));
                    _textBreak[ilTarget] = ilTarget;
                    //shell.Debugger.Processes.Active.Breakpoints;
                    breakTarget.UpdateUI();
                }
                else
                {
                    if (shell.Debugger.Processes.HaveActive)
                    {
                        int nBreak = -1;
                        /* hack count */
                        for (int i = 0; i<1000 ; ++i)
                        {
                            MDbgBreakpoint brk = shell.Debugger.Processes.Active.Breakpoints[i];
                            if (null != brk)
                            {
                                string location = brk.Location.ToString();
                                location = location.Replace("::", ".");
                                location = location.Replace("(", "");
                                location = location.Replace(")", "");
                                if (location == ilTarget)
                                {
                                    nBreak = brk.Number;
                                }
                            }
                        }
                        if (nBreak != -1)
                        {
                            ExecuteCommand(this, new CommandEventArgs("delete " +nBreak.ToString()));
                            breakTarget.RemoveBreakpointUI(ilTarget);
                            _textBreak.Remove(ilTarget);
                        }
                    }
                }
            }
            else
                System.Console.Beep();
        }
        public void Stop_Click(object sender, EventArgs e)
        {
            MDbgShell shell = Services.Get<MDbgShell>();
            Thread t = new Thread(delegate()
            {
                try
                {
                    shell.Debugger.Processes.Active.AsyncStop().WaitOne();
                    shell.Debugger.Processes.Active.CorProcess.Terminate(-1);
                    BreakState.OnBreak = false;
                }
                catch
                {
                }
            });
            t.SetApartmentState(ApartmentState.MTA);
            t.IsBackground = true;
            t.Start();
            t.Join();
        }
        public void Break_Click(object sender, EventArgs e)
        {
            DebuggerWorkItem dwi = WorkItems.Get<DebuggerWorkItem>("debugger");
            MDbgShell shell = Services.Get<MDbgShell>();
            BreakState.OnBreak = true;
            Thread t = new Thread(delegate()
            {
                try
                {
                    shell.Debugger.Processes.Active.AsyncStop().WaitOne();
                    dwi.ReflectFrame(null);
                }
                catch
                {
                    
                }
            });
            t.SetApartmentState(ApartmentState.MTA);
            t.IsBackground = true;
            t.Start();
            //t.Join();
        }
        public void StepInto_Click(object sender, EventArgs e)
        {
            BreakState.CanStep = false;
            ExecuteCommand(this, new CommandEventArgs("s"));
        }

        public void BreakOnException_Click(object sender, EventArgs e)
        {
            MDbgShell shell = Services.Get<MDbgShell>();
            shell.Debugger.Options.StopOnException = !shell.Debugger.Options.StopOnException;
            shell.IO.WriteOutput("<meta>", "Catch Exception is set to " + shell.Debugger.Options.StopOnException.ToString());
        }


        public void StepOut_Click(object sender, EventArgs e)
        {
            BreakState.CanStep = false;
            ExecuteCommand(this, new CommandEventArgs("o"));
        }
        public void StepOver_Click(object sender, EventArgs e)
        {
            BreakState.CanStep = false;
            ExecuteCommand(this, new CommandEventArgs("n"));
        }
        public void AttachProcess_Click(object sender, EventArgs e)
        {
            gui.AttachProcess form = new gui.AttachProcess();
            DialogResult dlg = form.ShowDialog();
            if (dlg == DialogResult.OK)
            {
                ExecuteCommand(this, new CommandEventArgs(string.Format("a {0}",form.SelectedPid)));
                BreakState.Attaching = true;
                BreakState.OnBreak = true;
            }
        }
        private class VisibilityConfiguration : IVisibilityConfiguration
        {
            public bool Public { get { return true; } }
            public bool Private { get { return true; } }
            public bool Family { get { return true; } }
            public bool Assembly { get { return true; } }
            public bool FamilyAndAssembly { get { return true; } }
            public bool FamilyOrAssembly { get { return true; } }
        }
        #region helpers
        private class LanguageWriterConfiguration : ILanguageWriterConfiguration
        {
            private IVisibilityConfiguration visibility = new VisibilityConfiguration();

            public IVisibilityConfiguration Visibility
            {
                get
                {
                    return this.visibility;
                }
            }

            public string this[string name]
            {
                get
                {
                    switch (name)
                    {
                        //case "ShowDocumentation":
                        case "ShowCustomAttributes":
                        case "ShowNamespaceImports":
                        case "ShowNamespaceBody":
                        case "ShowTypeDeclarationBody":
                        case "ShowMethodDeclarationBody":
                            return "true";
                    }

                    return "false";
                }
            }
        }
        private class AssemblyResolver : IAssemblyResolver
        {
            private System.Collections.IDictionary _assemblyTable;
            private IAssemblyResolver _assemblyResolver;

            public AssemblyResolver(IAssemblyResolver assemblyResolver)
            {
                _assemblyTable = new System.Collections.Hashtable();
                _assemblyResolver = assemblyResolver;
            }
            public IAssembly Resolve(IAssemblyReference assemblyName, string localPath)
            {
                if (_assemblyTable.Contains(assemblyName))
                {
                    return (IAssembly)_assemblyTable[assemblyName];
                }
                IAssembly assembly = _assemblyResolver.Resolve(assemblyName, localPath);
                _assemblyTable.Add(assemblyName, assembly);
                return assembly;
            }
        }
        #endregion
    }
}
